#ifndef QUATERNION_H
#define QUATERNION_H

// Quaternion of the form q = w + x*i + y*j + z*k.
typedef struct quaternion_t {
	vector3_t v; // x, y, z
	fixed_t w;
} quaternion_t;

// Returns the lenght of q.
fixed_t quaternion_length(const quaternion_t *q);

// Returns the length of q squared (faster than computing the length).
fixed_t quaternion_length_squared(const quaternion_t *q);

// Normalizes q and stores the result in out.
// Works for q == out.
void quaternion_normalize(const quaternion_t *q, quaternion_t *out);

// Computes the conjugate of q (w - x*i - y*j - z*k) and stores the result in out.
// Works for q == out.
void quaternion_conjugate(const quaternion_t *q, quaternion_t *out);

// Multiplies q1 by q2 and stores the result in out.
// Does not work for q1 == out or q2 == out.
void quaternion_mul(const quaternion_t *q1, const quaternion_t *q2, quaternion_t *out);

// Adds q2 to q1 and stores the result in out.
// Works for q1 == out or q2 == out.
void quaternion_add(const quaternion_t *q1, const quaternion_t *q2, quaternion_t *out);

// Subtracts q2 from q1 and stores the result in out.
// Works for q1 == out or q2 == out.
void quaternion_sub(const quaternion_t *q1, const quaternion_t *q2, quaternion_t *out);

// Computes the square of the quaternion (q^2).
// Does not work for q == out.
void quaternion_square(const quaternion_t *q, quaternion_t *out);

// Converts quaternion q to its rotation matrix form M.
void quaternion_to_matrix(const quaternion_t *q, matrix_t *M); // convert q to rotation matrix form M

// Rotates the vector v by the (unit) quaternion q and stores the result in out.
// If you are rotating more than one vector, it is faster to compute the rotation matrix
// and then transform the vectors by the matrix.
// Does not work for v == out.
void quaternion_rotate(const quaternion_t *q, const vector3_t *v, vector3_t *out);

// Rotates the vector v by the conjugate of the (unit) quaternion q and stores the result in out.
// If you are rotating more than one vector, it is faster to compute the rotation matrix
// and then transform the vectors by the transpose of the matrix.
// Does not work for v == out.
void quaternion_rotate_inv(const quaternion_t *q, const vector3_t *v, vector3_t *out);

// Computes the exponential of quaternion q and stores the result in out.
// Works for q == out.
void quaternion_exp(const quaternion_t *q, quaternion_t *out);

extern const quaternion_t QUATERNION_ZERO;

#endif
